"""
Parent class for all channels

Copyright (c) 2009 Christopher Hesse
http://code.google.com/p/python-expect/
"""

import select
import threading
import logging
from expect.expectable import Expectable
from .interface import ChannelInterface

log = logging.getLogger(__name__)


class BaseChannel(Expectable, ChannelInterface):
    """
    The base channel that we use for all the expect.channels Channels.
    
    All channels should inherit from both the ChannelInterface and Expectable.
    
    If you use Python's logging module with this library, your formatter can
    look at the 'channel' property of the record to add the channel to the
    log.  Look at expect.utility.ExpectLoggingFormatter for an example.
    """
    _instance_counter = 0
    _instance_counter_lock = threading.RLock()
    
    def __new__(cls, *args, **kwargs):
        # generate a name for this instance, or use the provided one
        instance = super(BaseChannel, cls).__new__(cls)
        class_name = cls.__name__
        
        # increment the instance counter
        with cls._instance_counter_lock:
            instance_number = cls._instance_counter
            cls._instance_counter += 1
        
        # set the instance name 
        if 'name' in kwargs:
            instance_name = kwargs['name']
        else:
            instance_name = "{class_name} #{instance_number}".format(**locals())
        instance._name = instance_name
            
        message = "Created number {instance_number} instance of {class_name} "\
                  "with name '{instance_name}'"
        log.debug(message.format(**locals()), **instance._log_info())
        return instance
        
    def __init__(self, name=None, **kwargs):
        super(BaseChannel, self).__init__(**kwargs)
        # all derived classes should pass **kwargs to their parent __init__ method
        # set the name even though it was set in the __new__ method
        # just in case
        self.name = name
        
    def _get_name(self):
        # accessor function for the name property
        return self._name
            
    def _set_name(self, name):
        # setter function for the name property, log a message that we are
        # changing the name as well
        if name is not None and name != self._name:
            log.debug("Changing name of channel '{0}' to '{1}'".format(name, self._name), extra={'channel':self.name})
            self._name = name
    
    name = property(_get_name, _set_name, 
                    doc="A string representing this channel, used for log messages.")
                    
    def _log_info(self):
        # this method is called when logging, to provide the channel information
        # to the logger, like so: log.debug("message", **self._log_info())
        return {'extra': {'channel': self.name}}
        
    def _log_read(self, data):
        # log a read operation from the channel
        log.info(data, extra={'channel': self.name, 'direction': 'read'})

    def _log_write(self, data):
        # log a write operation to the channel
        log.info(data, extra={'channel': self.name, 'direction': 'write'})
        
    def __repr__(self):
        return "<{0} name='{1}'>".format(self.__class__.__name__, self.name)
        
    def wait_readable(self, timeout=None):
        """
        Wait for the channel to become readable. If no timeout is specified,
        wait forever. If a timeout of 0 is used, just poll and return
        immediately.
        """
        if timeout is None:
            readable, _writable, _other = select.select([self], [], [])
        else:
            readable, _writable, _other = select.select([self], [], [], timeout)
        return len(readable) == 1
    
    @property
    def is_readable(self):
        """
        Return True if the channel is currently readable, False otherwise.
        """
        return self.wait_readable(0)